package com.zjk.storage.core.provider.amazonS3;

import cn.hutool.core.util.StrUtil;
import com.zjk.storage.core.entity.BaseFileInfo;
import com.zjk.storage.core.entity.UploadPretreatment;
import com.zjk.storage.core.exception.FileStorageException;
import com.zjk.storage.core.provider.FileStorage;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain;
import software.amazon.awssdk.awscore.exception.AwsServiceException;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.function.Consumer;


@Slf4j
public class AmazonS3Storage implements FileStorage {
    private AmazonS3Config config;
    private S3Client client;
    private String bucketName;
    private String domain;
    private String basePath;

    public AmazonS3Storage(AmazonS3Config config) {
        this.config = config;
        this.bucketName = config.getBucketName();
        this.domain = StrUtil.addSuffixIfNot(config.getDomain(), StrUtil.EMPTY);
        this.basePath = StrUtil.addSuffixIfNot(config.getBasePath(), StrUtil.EMPTY);

        initAmazonS3Client();

        if (config.isBucketAutoCreate()) {
            createBucket();
        }
    }

    private void createBucket() {
        try {
            CreateBucketRequest bucketRequest = CreateBucketRequest.builder()
                    .bucket(bucketName)
                    .build();
            client.createBucket(bucketRequest);
        } catch (Exception e) {
            log.error("{}创建bucket:{}失败",config.getPlatform(),bucketName,e);
        }

    }

    private void initAmazonS3Client() {
        String region = config.getRegion();
        String endpoint = config.getEndpoint();
        AwsCredentialsProviderChain credentialsProviderChain = AwsCredentialsProviderChain.builder()
                .addCredentialsProvider(() -> AwsBasicCredentials.create(config.getAccessKey(), config.getSecretKey())).build();
        this.client = S3Client.builder()
                .credentialsProvider(credentialsProviderChain)
                .region(Region.of(region))
                .endpointOverride(URI.create(endpoint))
                .build();
    }

    @Override
    public String getPlatform() {
        return config.getPlatform();
    }

    @Override
    public boolean upload(BaseFileInfo fileInfo, UploadPretreatment pre) {
        String key = basePath + fileInfo.getPath() + fileInfo.getFileName();
        fileInfo.setBasePath(basePath);
        fileInfo.setUrl(domain + key);
        try {
            putObject(bucketName, key, pre.getFileWrapper().getInputStream());
            byte[] thumbnailBytes = pre.getThumbnailBytes();
            if (thumbnailBytes != null) {
                String thKey = basePath + fileInfo.getPath() + fileInfo.getThumbFilename();
                putObject(bucketName, thKey, new ByteArrayInputStream(thumbnailBytes));
            }
            return true;
        } catch (IOException e) {
            //删除上传过程中产生的文件
            client.deleteObject(DeleteObjectRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .build());
            log.error("platform:{},上传文件失败！ fileName:{},",config.getPlatform(),fileInfo.getOriginalName(),e);
            throw new FileStorageException("上传文件失败！",e);
        }

    }

    @SneakyThrows
    private void putObject(String bucketName, String key, InputStream inputStream) {
        PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                .bucket(bucketName)
                .key(key)
                .build();
        client.putObject(putObjectRequest, RequestBody.fromInputStream(inputStream, inputStream.available()));
    }

    @Override
    public boolean delete(BaseFileInfo fileInfo) {
        if (fileInfo.getThumbFilename() != null) {
            client.deleteObject(DeleteObjectRequest.builder()
                    .bucket(bucketName)
                    .key(basePath + fileInfo.getPath() + fileInfo.getThumbFilename())
                    .build());
        }
        client.deleteObject(DeleteObjectRequest.builder()
                .bucket(bucketName)
                .key(basePath + fileInfo.getPath() + fileInfo.getFileName())
                .build());
        return true;
    }

    @Override
    public boolean exists(BaseFileInfo fileInfo) {
        ResponseInputStream<GetObjectResponse> object = client.getObject(GetObjectRequest.builder()
                .bucket(bucketName)
                .key(basePath + fileInfo.getPath() + fileInfo.getFileName())
                .build());
        return object != null && object.response() != null && object.response().deleteMarker();
    }

    @Override
    public void download(BaseFileInfo fileInfo, Consumer<InputStream> consumer) {
        getObject(bucketName, basePath + fileInfo.getPath() + fileInfo.getFileName(), consumer);
    }

    @Override
    public void downloadTh(BaseFileInfo fileInfo, Consumer<InputStream> consumer) {
        getObject(bucketName, basePath + fileInfo.getPath() + fileInfo.getThumbFilename(), consumer);
    }

    void getObject(String bucketName, String key , Consumer<InputStream> consumer) {
        GetObjectRequest getObjectResponse = GetObjectRequest.builder()
                .key(key)
                .bucket(bucketName)
                .build();
        try (InputStream in = client.getObject(getObjectResponse)) {
            consumer.accept(in);
        } catch (IOException | SdkClientException | AwsServiceException e) {
            log.error("platform:{},下载文件失败！ ,",config.getPlatform(),e);
            throw new FileStorageException("下载文件失败！",e);
        }
    }

}
